﻿using System;
using System.Xml.Linq;

namespace SPContrib.Extensions
{
    /// <summary>
    /// Extensions for the XAttribute class
    /// </summary>
    public static class XAttributeExtensions
    {
        /// <summary>
        /// Extracts and converts the attribute´s value to another type
        /// </summary>
        /// <typeparam name="T">the type to convert the attribute´s value to</typeparam>
        /// <exception cref="System.Exception" />
        public static T ValueAs<T>(this XAttribute instance)
        {
            // conditions:
            // no converter function (i.e. if no mapping exists an exception is thrown)
            // no default value to use
            ValidateTypeMappingFor<T>(instance);
            return TypeMapper.Resolve<string, T>(instance.Value);            
        }

        /// <summary>
        /// Extracts and converts the attribute´s value to another type
        /// </summary>
        /// <typeparam name="T">the type to convert the attribute´s value to</typeparam>
        /// <param name="valueIfNotExists">the value to return if the attribute is either null or an empty string</param>
        public static T ValueAs<T>(this XAttribute instance, T valueIfNotExists)
        {
            if (instance.IsNullOrEmpty())
            {
                return valueIfNotExists;
            }
            return instance.ValueAs<T>();
        }

        /// <summary>
        /// Extracts and converts the attribute´s value to another type
        /// </summary>
        /// <typeparam name="T">the type to convert the attribute´s value to</typeparam>
        /// <param name="converter">represents a method that converts from a string to another type</param>
        /// <exception cref="System.Exception" />
        public static T ValueAs<T>(this XAttribute instance, Func<string, T> converter)
        {
            if(TypeMapper.CannotResolveBetween<string,T>())
            {
                TypeMapper.RegisterMappingBetween<string,T>(converter);
            }
            ValidateTypeMappingFor<T>(instance);
            return TypeMapper.Resolve<string, T>(instance.Value);
        }

        private static bool IsNullOrEmpty(this XAttribute instance)
        {
            return instance.IsNull() || instance.Value.IsNullOrEmpty();
        }

        private static void ValidateTypeMappingFor<T>(this XAttribute instance)
        {
            if (instance.IsNull())
            {
                throw "The attribute does not exists (i.e. the instance of it is null)"
                        .AsException();
            }
            else if (instance.Value.IsNullOrEmpty())
            {
                throw "The attribute '{0}' has no value (i.e. the value is either null or an empty string)"
                        .AsException(instance.Name.LocalName);
            }
            else if (TypeMapper.CannotResolveBetween<string,T>())
            {
                throw "No mapping exists between 'XAttribute (string)' and '{0}'. Use the overloaded ValueAs<T> to supply your own mapping method between 'XAttribute (string)' and '{0}'"
                        .AsException(typeof(T).Name);
            }
        }
    }

}
